package tk.winshu.shortestpath.operate;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tk.winshu.shortestpath.model.Node;
import tk.winshu.shortestpath.model.NodeData;
import tk.winshu.shortestpath.model.NodeStatus;

import java.awt.*;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * 节点管理器，存放操作记录
 * <p>
 * 用于动作回放
 *
 * @author winshu
 * @since 2019/10/28
 */
public class NodeManager {

    private static final Logger log = LoggerFactory.getLogger(NodeManager.class);
    private static final int MAX_OPERATE_SIZE = 50;

    private final List<Node> nodes = new ArrayList<>();
    private final LinkedList<IOperate> operates = new LinkedList<>();
    private final LinkedList<IOperate> caches = new LinkedList<>();

    /**
     * 起点节点
     */
    private Node startNode;

    /**
     * 终点节点
     */
    private Node endNode;

    /**
     * 焦点节点
     */
    private Node focusedNode;

    public void clear() {
        startNode = null;
        endNode = null;
        focusedNode = null;

        nodes.clear();
        operates.clear();
        caches.clear();
    }

    /**
     * 执行操作
     */
    public void execute(IOperate operate) {
        operate.init(nodes);
        boolean result = operate.execute();

        // 如果操作失败，不记入历史，不打印日志
        if (!result) {
            return;
        }

        // 如果缓存非空，则丢弃
        if (!caches.isEmpty()) {
            caches.clear();
        }
        // 如果操作超过最大数量，则丢弃
        if (operates.size() == MAX_OPERATE_SIZE) {
            operates.removeLast();
        }
        operates.push(operate);

        // log
        OperateType operateType = operate.getClass().getAnnotation(OperateType.class);
        if (operateType != null) {
            log.info(operateType.name());
        }
    }

    public boolean historyToNext() {
        if (caches.isEmpty()) {
            return false;
        }
        IOperate operate = caches.pop();
        operates.push(operate);
        operate.execute();
        return true;
    }

    /**
     * 回退一步
     */
    public boolean historyToPrevious() {
        if (operates.isEmpty()) {
            return false;
        }
        IOperate operate = operates.pop();
        caches.push(operate);
        operate.revert();
        return true;
    }

    public List<Node> getNodes() {
        return nodes;
    }

    /**
     * 获取指定位置周边的节点
     */
    public Node getNode(Point2D point) {
        if (point == null) {
            return null;
        }

        for (Node node : nodes) {
            if (node.isContainsPoint(point)) {
                return node;
            }
        }
        return null;
    }

    public boolean isEmpty() {
        return nodes.isEmpty();
    }

    public Node getFirstNode() {
        return nodes.isEmpty() ? null : nodes.get(0);
    }

    public Node getStartNode() {
        return startNode;
    }

    public void setStartNode(Node startNode) {
        this.startNode = startNode;
    }

    public Node getEndNode() {
        return endNode;
    }

    public void setEndNode(Node endNode) {
        this.endNode = endNode;
    }

    public Node getFocusedNode() {
        return focusedNode;
    }

    public boolean setFocusedNode(Node focusedNode) {
        if (this.focusedNode == focusedNode) {
            return false;
        }
        this.focusedNode = focusedNode;
        return true;
    }

    public boolean markFocusedAsEndNode() {
        if (focusedNode == null) {
            return false;
        }
        if (this.startNode == focusedNode) {
            this.startNode = null;
        }
        this.endNode = focusedNode;
        return true;
    }

    public boolean markFocusedAsStartNode() {
        if (focusedNode == null) {
            return false;
        }
        if (this.endNode == focusedNode) {
            this.endNode = null;
        }
        this.startNode = focusedNode;
        return true;
    }

    public boolean isReady() {
        if (startNode == endNode) {
            return false;
        }
        if (startNode == null || endNode == null) {
            return false;
        }
        int count = 0;
        for (Node node : nodes) {
            count += node.getEndNodes().size();
        }
        return count > 0;
    }

    public NodeData buildNodeData() {
        NodeData nodeConfig = new NodeData();
        nodeConfig.setNodes(nodes);
        nodeConfig.setStartIndex(startNode == null ? 0 : startNode.getIndex());
        nodeConfig.setEndIndex(endNode == null ? 0 : endNode.getIndex());
        return nodeConfig;
    }

    public void exchange() {
        Node tempNode = startNode;
        startNode = endNode;
        endNode = tempNode;
    }

    public void drawNode(Graphics2D g2d) {
        for (Node node : nodes) {
            if (node.equals(focusedNode)) {
                node.draw(g2d, NodeStatus.FOCUSED);
            } else if (node.equals(startNode)) {
                node.draw(g2d, NodeStatus.START);
            } else if (node.equals(endNode)) {
                node.draw(g2d, NodeStatus.END);
            } else {
                node.draw(g2d, NodeStatus.DEFAULT);
            }
        }
    }
}
